package photo

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/ctxdatax"
	"mall/common/globalkey"
	"mall/common/moneyx"
	"mall/common/mqueuex"
	"mall/common/resultx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/blog/rpc/blogclient"
	"mall/service/forum/api/internal/lib/userrank"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/api/internal/utils/parse"
	"mall/service/forum/rpc/forumclient"
	"mall/service/user/rpc/userclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetPhotoListLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetPhotoListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPhotoListLogic {
	return &GetPhotoListLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetPhotoListLogic) GetPhotoList(req *types.PhotoListReq) (*types.PhotoListInfo, error) {
	userId := ctxdatax.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	var uid uint64
	if req.UserId != nil && *req.UserId > 0 {
		uid = *req.UserId
	} else {
		uid = userId
	}
	var private bool = true
	if uid != userId {
		private = false
	}

	page, pageSize := parse.ParsePageInfo(
		req.Current,
		req.PageSize,
		l.svcCtx.Config.Query.DefaultAmount,
		l.svcCtx.Config.Query.MaxAmount,
	)

	var filters []string
	filters = append(filters, "ForumId,equalTo:"+strconv.FormatInt(l.svcCtx.Config.Query.PhotoForumId, 10))
	if req.Filter != nil && len(strings.TrimSpace(*req.Filter)) > 0 {
		filterMap := util.GetFilterMap(strings.TrimSpace(*req.Filter))
		for key, val := range filterMap {
			switch key {
			case "sticky": // todo: 使用场景未知
				filters = append(filters, "Sticky,equalTo:"+val)
			}
		}
	}

	var preload string = "forumPost:Id,AuthorId,ParentId"
	var eagerLoad string = "forumPost:forumMedia"
	var skipParent bool
	var skipLevel bool
	if req.ParentId != nil && *req.ParentId > 0 { // 指定香水
		private = false // 大家的
		skipParent = true
		preload += ",AuthorName,AuthorAvatar,AuthorIp,AuthorLevel"
		eagerLoad += ",photoTotal"
		filters = append(filters, "ParentId,equalTo:"+strconv.FormatInt(int64(*req.ParentId), 10))
		filters = append(filters, "Status,equalTo:"+strconv.FormatInt(int64(globalkey.StatusEnabled), 10))
	} else if private { // 我的图片
		skipLevel = true
		preload += ",Status"
		eagerLoad += ",photoTotal"
		filters = append(filters, "AuthorId,equalTo:"+strconv.FormatInt(int64(uid), 10))
		filters = append(filters, "Status,notEqualTo:"+strconv.FormatInt(int64(globalkey.StatusDeleted), 10))
	} else { // 别人的
		skipLevel = true
		filters = append(filters, "AuthorId,equalTo:"+strconv.FormatInt(int64(uid), 10))
		filters = append(filters, "Status,equalTo:"+strconv.FormatInt(int64(globalkey.StatusEnabled), 10))
	}
	preload += ",ContentHtml,CommentStatus,CommentCount,CreatedAt;forumMedia:Id,Uid,Url,Thumbnail,Order"

	var sorter *string
	if req.Sorter != nil {
		sorter = pointy.GetPointer(strings.TrimSpace(*req.Sorter))
	}
	var platformId uint32 = globalkey.ThirdPartyPlatformWechatMini

	res, err := l.svcCtx.ForumRpc.GetPhotoList(l.ctx, &forumclient.BaseListReq{
		Offset:     uint32((page - 1) * pageSize),
		Limit:      uint32(pageSize),
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		Sorter:     sorter,
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
	})
	if err != nil {
		return nil, err
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}
	resp := &types.PhotoListInfo{
		BaseListInfo: types.BaseListInfo{
			Total:           res.Total,
			Current:         page,
			PageSize:        pageSize,
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	if len(res.List) > 0 {
		var perfumeMap map[uint64]*types.KbEntryInfo
		if !skipParent {
			var ids []string
			for _, v := range res.List {
				if v.ParentId != nil && *v.ParentId > 0 {
					ids = append(ids, strconv.FormatInt(int64(*v.ParentId), 10))
				}
			}

			if len(ids) == 0 {
				return resp, nil
			}

			perfumeMap, err = l.getPerfumeMap(ids)
			if err != nil {
				return nil, err
			}

			if perfumeMap == nil {
				return resp, nil
			}
		}

		var userRankMap map[uint32]*types.UserRankInfo
		if !skipLevel {
			userRankMap = l.getUserRankMap(userId)
		}

		for _, v := range res.List {
			var parent *types.KbEntryInfo
			if !skipParent && perfumeMap != nil {
				if pv, ok := perfumeMap[*v.ParentId]; ok {
					parent = pv
				}
			}

			var photos []*types.MediaInfo
			if len(v.Media) > 0 {
				for _, m := range v.Media {
					var url string
					if m.Url != nil && len(*m.Url) > 0 {
						url = *m.Url
					}
					if len(url) == 0 && m.Uri != nil && len(*m.Uri) > 0 {
						url = *m.Uri
					}
					var thumbnail *bool
					if m.Thumbnail != nil {
						thumbnail = pointy.GetPointer(*m.Thumbnail > 0)
					}
					photos = append(photos, &types.MediaInfo{
						Uid:       m.Uid,
						Id:        m.Id,
						Url:       pointy.GetPointer(url),
						Thumbnail: thumbnail,
						AltText:   m.AltText,
						Details:   m.Meta,
						Order:     m.Order,
					})
				}
			}

			var statusText *string
			var author *types.AuthorInfo
			if private {
				if v.Status != nil {
					statusText = pointy.GetPointer(util.GetStatusText(int32(*v.Status)))
				}
			} else if !skipLevel && userRankMap != nil {
				var levelName *string
				var levelUrl *string
				if userRankMap != nil && v.Author != nil && v.Author.Level != nil && *v.Author.Level > 0 {
					if userRank, ok := userRankMap[*v.Author.Level]; ok {
						levelName = userRank.Name
						levelUrl = userRank.MediaUrl
					}
				}
				if v.Author != nil && v.Author.Name != nil {
					var anonymous *bool
					if v.Author.Anonymous != nil {
						anonymous = pointy.GetPointer(*v.Author.Anonymous > 0)
					}
					author = &types.AuthorInfo{
						Id:        v.Author.Id,
						Name:      v.Author.Name,
						Avatar:    v.Author.Avatar,
						Ip:        v.Author.Ip,
						Level:     v.Author.Level,
						Anonymous: anonymous,
						LevelName: levelName,
						LevelUrl:  levelUrl,
					}
				}
			}

			var commentStatus *bool
			if v.CommentStatus != nil {
				commentStatus = pointy.GetPointer(*v.CommentStatus > 0)
			}
			var createdAt *string
			if v.CreatedAt != nil && len(*v.CreatedAt) > 0 {
				createdAt = pointy.GetPointer(util.GetTimeFromNow(*v.CreatedAt))
			}

			resp.List = append(resp.List, &types.PhotoPostInfo{
				Id:            v.Id,
				ParentId:      v.ParentId,
				Parent:        parent,
				Photos:        photos,
				Status:        v.Status,
				StatusText:    statusText,
				CommentStatus: commentStatus,
				CommentCount:  v.CommentCount,
				Author:        author,
				CreatedAt:     createdAt,
				UpdatedAt:     v.CreatedAt,
			})
		}
	}

	if private && req.New != nil && *req.New {
		var userMeta []*userclient.UserMetaInfo
		userMeta = append(userMeta, &userclient.UserMetaInfo{
			Key:   pointy.GetPointer(mqueuex.UserStatisticForumPhotoNew), // "_photo_new_status"
			Value: pointy.GetPointer("0"),
		})
		l.svcCtx.UserRpc.SetUserMeta(l.ctx, &userclient.SetUserMetaReq{
			UserId:   pointy.GetPointer(userId),
			UserMeta: userMeta,
		})
	}

	return resp, nil
}

func (l *GetPhotoListLogic) getPerfumeMap(ids []string) (map[uint64]*types.KbEntryInfo, error) {
	if len(ids) == 0 {
		return nil, nil
	}

	var preload string = "blogBook:Id,Title,Slug;blogMedia:Id,Uid,Url,Thumbnail;blogBookMeta:year,averageRating;blogBookRelation:brand"
	var eagerLoad string = "blogBook:blogMedia,blogBookMeta,blogBookRelation"
	res, err := l.svcCtx.BlogRpc.BookList(l.ctx, &blogclient.BookListReq{
		Preload:   preload,
		EagerLoad: eagerLoad,
		EntryIds:  strings.Join(ids, ","),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("GetPostList getPerfumeMap err: %+v", err))
		return nil, err
	}

	if len(res.Data) > 0 {
		resp := make(map[uint64]*types.KbEntryInfo)
		for _, item := range res.Data {
			var media []*types.TmpMediaInfo
			if len(item.Media) > 0 {
				for _, v := range item.Media {
					media = append(media, &types.TmpMediaInfo{
						Uid:       pointy.GetPointer(uint64(v.Uid)),
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.Id), 10)),
						Url:       pointy.GetPointer(v.Url),
						Thumbnail: pointy.GetPointer(uint32(v.Thumbnail)),
						AltText:   pointy.GetPointer(v.AltText),
						MetaData:  pointy.GetPointer(v.Meta),
					})
				}
			}
			var brand []*types.KbEntryInfo
			if len(item.Relations) > 0 {
				for _, v := range item.Relations {
					if v.Section == "brand" {
						brand = append(brand, &types.KbEntryInfo{
							Id:    pointy.GetPointer(uint64(v.Id)),
							Title: pointy.GetPointer(v.Title),
							Slug:  pointy.GetPointer(v.Slug),
						})
					}
				}
			}
			var year *string
			var averageRating *string
			if len(item.Meta) > 0 {
				for _, v := range item.Meta {
					switch v.Key {
					case "year":
						year = pointy.GetPointer(v.Value)
					case "averageRating":
						averageRating = pointy.GetPointer(moneyx.ParseStar(v.Value, 1))
					}
				}
			}

			resp[uint64(item.Id)] = &types.KbEntryInfo{
				Id:            pointy.GetPointer(uint64(item.Id)),
				Title:         pointy.GetPointer(item.Title),
				Slug:          pointy.GetPointer(item.Slug),
				Year:          year,
				AverageRating: averageRating,
				Media:         media,
				Brands:        brand,
			}
		}

		return resp, nil
	}

	return nil, nil
}

func (l *GetPhotoListLogic) getUserRankMap(userId uint64) map[uint32]*types.UserRankInfo {
	userRank := userrank.NewUserRank(l.ctx, l.svcCtx)
	userRankList, err := userRank.List(userId)
	if err != nil {
		logrus.Info(fmt.Sprintf("GetPostList getUserRankMap err: %+v", err))
		return nil
	}

	if len(userRankList) > 0 {
		resp := make(map[uint32]*types.UserRankInfo)
		for _, v := range userRankList {
			if v.Id != nil && *v.Id > 0 {
				resp[uint32(*v.Id)] = v
			}
		}

		return resp
	}

	return nil
}
